// src/utils/snowflakeIdGenerator.js

class SnowflakeIdGenerator {
    constructor(workerId, datacenterId) {
        this.workerId = workerId % 32; // Worker ID (0~31)
        this.datacenterId = datacenterId % 32; // Datacenter ID (0~31)
        this.sequence = 0n; // Sequence number (0~4095)
        this.lastTimestamp = -1n; // Last timestamp

        const twepoch = 1288834974657n; // Epoch start time, units: milliseconds
        const workerIdBits = 5n; // Bits for worker ID
        const datacenterIdBits = 5n; // Bits for datacenter ID
        const sequenceBits = 12n; // Bits for sequence number

        this.maxWorkerId = -1n ^ ((-1n) << workerIdBits);
        this.maxDatacenterId = -1n ^ ((-1n) << datacenterIdBits);
        this.sequenceMask = -1n ^ ((-1n) << sequenceBits);

        this.workerIdShift = sequenceBits;
        this.datacenterIdShift = sequenceBits + workerIdBits;
        this.timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

        if (this.workerId > this.maxWorkerId || this.workerId < 0) {
            throw new Error(`Worker ID can't be greater than ${this.maxWorkerId} or less than 0`);
        }
        if (this.datacenterId > this.maxDatacenterId || this.datacenterId < 0) {
            throw new Error(`Datacenter ID can't be greater than ${this.maxDatacenterId} or less than 0`);
        }
    }

    tilNextMillis(lastTimestamp) {
        let timestamp = BigInt(Date.now());
        while (timestamp <= lastTimestamp) {
            timestamp = BigInt(Date.now());
        }
        return timestamp;
    }

    nextId() {
        let timestamp = BigInt(Date.now());

        if (this.lastTimestamp === timestamp) {
            this.sequence = (this.sequence + 1n) & this.sequenceMask;
            if (this.sequence === 0n) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0n;
        }

        if (timestamp < this.lastTimestamp) {
            throw new Error(`Clock moved backwards. Refusing to generate id for ${this.lastTimestamp - timestamp}`);
        }

        this.lastTimestamp = timestamp;

        return ((timestamp - BigInt(1288834974657)) << this.timestampLeftShift) |
            (BigInt(this.datacenterId) << this.datacenterIdShift) |
            (BigInt(this.workerId) << this.workerIdShift) |
            this.sequence;
    }
}

const generator = new SnowflakeIdGenerator(1, 1);

export function nextId() {
    return generator.nextId().toString();
}